#include "OgreStableHeaders.h"
#include "OgreColourValue.h"
#include "OgreMath.h"

namespace Ogre
{
	const ColourValue ColourValue::ZERO = ColourValue(0.0, 0.0, 0.0, 0.0);
	const ColourValue ColourValue::Black = ColourValue(0.0, 0.0, 0.0);
	const ColourValue ColourValue::White = ColourValue(1.0, 1.0, 1.0);
	const ColourValue ColourValue::Red = ColourValue(1.0, 0.0, 0.0);
	const ColourValue ColourValue::Green = ColourValue(0.0, 1.0, 0.0);
	const ColourValue ColourValue::Blue = ColourValue(0.0, 0.0, 1.0);

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	ABGR ColourValue::getAsABGR(void) const
#else
	RGBA ColourValue::getAsRGBA(void) const
#endif
	{
		uint8 val8;
		uint32 val32 = 0;

		val8 = static_cast<uint8>(r * 255);
		val32 = val8 << 24;

		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 16;

		val8 = static_cast<uint8>(b * 255);
		val32 += val8 << 8;

		val8 = static_cast<uint8>(a * 255);
		val32 += val8;

		return val32;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	BGRA ColourValue::getAsBGRA(void) const
#else
	ARGB ColourValue::getAsARGB(void) const
#endif
	{
		uint8 val8;
		uint32 val32 = 0;

		val8 = static_cast<uint8>(a * 255);
		val32 = val8 << 24;

		val8 = static_cast<uint8>(r * 255);
		val32 += val8 << 16;

		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 8;

		val8 = static_cast<uint8>(b * 255);
		val32 += val8;

		return val32;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	ARGB ColourValue::getAsARGB(void) const
#else
	BGRA ColourValue::getAsBGRA(void) const
#endif
	{
		uint8 val8;
		uint32 val32 = 0;

		val8 = static_cast<uint8>(b * 255);
		val32 = val8 << 24;

		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 16;

		val8 = static_cast<uint8>(r * 255);
		val32 += val8 << 8;

		val8 = static_cast<uint8>(a * 255);
		val32 += val8;

		return val32;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	RGBA ColourValue::getAsRGBA(void) const
#else
	ABGR ColourValue::getAsABGR(void) const
#endif
	{
		uint8 val8;
		uint32 val32 = 0;

		val8 = static_cast<uint8>(a * 255);
		val32 = val8 << 24;

		val8 = static_cast<uint8>(b * 255);
		val32 += val8 << 16;

		val8 = static_cast<uint8>(g * 255);
		val32 += val8 << 8;

		val8 = static_cast<uint8>(r * 255);
		val32 += val8;

		return val32;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	void ColourValue::setAsABGR(const ABGR val)
#else
	void ColourValue::setAsRGBA(const RGBA val) 
#endif
	{
		uint32 val32 = val;

		r = ((val32 >> 24) & 0xFF) / 255.0f;
		g = ((val32 >> 16) & 0xFF) / 255.0f;
		b = ((val32 >> 8) & 0xFF) / 255.0f;
		a = (val32 & 0xFF) / 255.0f;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	void ColourValue::setAsBGRA(const BGRA val)
#else
	void ColourValue::setAsARGB(const ARGB val)
#endif
	{
		uint32 val32 = val;

		a = ((val32 >> 24) & 0xFF) / 255.0f;
		r = ((val32 >> 16) & 0xFF) / 255.0f;
		g = ((val32 >> 8) & 0xFF) / 255.0f;
		b = (val32 & 0xFF) / 255.0f;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	void ColourValue::setAsARGB(const ARGB val)
#else
	void ColourValue::setAsBGRA(const BGRA val)
#endif
	{
		uint32 val32 = val;

		b = ((val32 >> 24) & 0xFF) / 255.0f;
		g = ((val32 >> 16) & 0xFF) / 255.0f;
		r = ((val32 >> 8) & 0xFF) / 255.0f;
		a = (val32 & 0xFF) / 255.0f;
	}

#if OGRE_ENDIAN == OGRE_ENDIAN_BIG
	void ColourValue::setAsRGBA(const RGBA val)
#else
	void ColourValue::setAsABGR(const ABGR val)
#endif
	{
		uint32 val32 = val;

		a = ((val32 >> 24) & 0xFF) / 255.0f;
		b = ((val32 >> 16) & 0xFF) / 255.0f;
		g = ((val32 >> 8) & 0xFF) / 255.0f;
		r = (val32 & 0xFF) / 255.0f;
	}

	bool ColourValue::operator==(const ColourValue& rhs) const
	{
		return (r == rhs.r &&
			g == rhs.g &&
			b == rhs.b &&
			a == rhs.a);
	}

	bool ColourValue::operator!=(const ColourValue& rhs) const
	{
		return !(*this == rhs);
	}

	void ColourValue::setHSB(Real hue, Real saturation, Real brightness)
	{
		if (hue > 1.0f)
		{
			hue -= (int)hue;
		}
		else if (hue < 0.0f)
		{
			hue += (int)hue + 1;
		}
		saturation = std::min(saturation, (Real)1.0);
		saturation = std::max(saturation, (Real)0.0);
		brightness = std::min(brightness, (Real)1.0);
		brightness = std::max(brightness, (Real)0.0);

		if (brightness == 0.0f)
		{
			r = g = b = 0.0f;
			return;
		}

		if (saturation == 0.0f)
		{
			r = g = b = brightness;
			return;
		}

		Real hueDomain = hue * 6.0f;
		if (hueDomain >= 6.0f)
		{
			hueDomain = 0.0f;
		}
		unsigned short domain = (unsigned short)hueDomain;
		Real f1 = brightness * (1 - saturation);
		Real f2 = brightness * (1 - saturation * (hueDomain - domain));
		Real f3 = brightness * (1 - saturation * (1 - (hueDomain - domain)));

		switch(domain)
		{
		case 0:
			r = brightness;
			g = f3;
			b = f1;
			break;
		case 1:
			r = f2;
			g = brightness;
			b = f1;
			break;
		case 2:
			r = f1;
			g = brightness;
			b = f3;
			break;
		case 3:
			r = f1;
			g = f2;
			b = brightness;
			break;
		case 4:
			r = f3;
			g = f1;
			b = brightness;
			break;
		case 5:
			r = brightness;
			g = f1;
			b = f2;
			break;
		}
	}

	void ColourValue::getHSB(Real* hue, Real* saturation, Real* brightness) const
	{
		Real vMin = std::min(r, std::min(g, b));
		Real vMax = std::max(r, std::max(g, b));
		Real delta = vMax - vMin;

		*brightness = vMax;

		if (Math::RealEqual(delta, 0.0f, 1e-6))
		{
			*hue = 0;
			*saturation = 0;
		}
		else
		{
			*saturation = delta / vMax;

			Real deltaR = (((vMax - r) / 6.0f) + (delta / 2.0f)) / delta;
			Real deltaG = (((vMax - g) / 6.0f) + (delta / 2.0f)) / delta;
			Real deltaB = (((vMax - b) / 6.0f) + (delta / 2.0f)) / delta;

			if (Math::RealEqual(r, vMax))
				*hue = deltaB - deltaG;
			else if (Math::RealEqual(g, vMax))
				*hue = 0.3333333f + deltaR - deltaB;
			else if (Math::RealEqual(b, vMax))
				*hue = 0.6666667f + deltaG - deltaR;

			if (*hue < 0.0f)
				*hue += 1.0f;
			if (*hue > 1.0f)
				*hue -= 1.0f;
		}
	}
}
